package net.lulihu.pangolin.headless.common.util;


/**
 * 使用SnowFlake算法生成id的结果是一个18位的整数
 */
public class IDGeneratorKit {

    private final static IdGenerator generator = IdGeneratorEnum.instance.getInstance();

    private IDGeneratorKit() {
        // 私有化构造函数
    }

    /**
     * 获取id
     */
    public static Long get() {
        return generator.nextId();
    }

    /**
     * 获取id
     */
    public static String getStr() {
        return String.valueOf(generator.nextId());
    }

    private static class IdGenerator {
        private long workerId;
        private long datacenterId;
        private long sequence = 0;

        private IdGenerator() {
            final ApparatusUtils apparatusUtils = new ApparatusUtils();
            final long[] apparatusId = apparatusUtils.getApparatusId();
            long workerId = apparatusId[0];
            long maxWorkerId = ~(-1L << workerIdBits);
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id 不能大于 %d 或者小于 0", maxWorkerId));
            }
            long datacenterId = apparatusId[1];
            long maxDatacenterId = ~(-1L << datacenterIdBits);
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id 不能大于 %d 或者小于 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        private long workerIdBits = 5L;
        private long datacenterIdBits = 5L;
        private long sequenceBits = 12L;

        private long workerIdShift = sequenceBits;
        private long datacenterIdShift = sequenceBits + workerIdBits;
        private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        private long sequenceMask = ~(-1L << sequenceBits);

        private long lastTimestamp = -1L;

        private synchronized long nextId() {
            long timestamp = timeGen();
            // 生成的时间戳比上次的时间戳还小，出错
            if (timestamp < lastTimestamp) {
                throw new RuntimeException("时钟倒退。 拒绝为 " + (lastTimestamp - timestamp) + " 毫秒生成ID");
            }
            // 生成的时间戳跟上次的时间戳一样，则需要生成一个sequence序列号
            if (lastTimestamp == timestamp) {
                // sequence循环自增
                sequence = (sequence + 1) & sequenceMask;
                // 如果sequence=0则需要重新生成时间戳
                if (sequence == 0) {
                    // 且必须保证时间戳序列往后
                    timestamp = tilNextMillis(lastTimestamp);
                }
                // 如果 timestamp > lastTimestamp，时间戳序列已经不同了，此时可以不必生成sequence了，直接取0
            } else {
                sequence = 0;
            }
            // 更新lastTimestamp时间戳
            lastTimestamp = timestamp;
            long twepoch = 1288834974657L;
            return ((timestamp - twepoch) << timestampLeftShift) |
                    (datacenterId << datacenterIdShift) |
                    (workerId << workerIdShift) |
                    sequence;
        }

        private long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }

        private long timeGen() {
            return System.currentTimeMillis();
        }

    }

    private static class ApparatusUtils {

        private final String[] binaryArray = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
                "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};

        /**
         * 获取机器id
         * 分为 workerId或dateCenterId
         */
        private long[] getApparatusId() {
            // cpu 16进制 序列号
            final String processorId = NativeKit.CPUSerialNumber();
            // 装换成 2进制字符码
            final StringBuilder builder = bytes2BitStr(hexStringToByte(processorId));
            // 取前10位 换算成 workerId  datacenterId
            long workerId = Integer.valueOf(builder.substring(0, 4), 2);
            long datacenterId = Integer.valueOf(builder.substring(5, 9), 2);
            return new long[]{workerId, datacenterId};
        }

        /**
         * 十六进制转二进制
         */
        private byte[] hexStringToByte(String hexString) {
            //hexString的长度对2取整，作为bytes的长度
            int len = hexString.length() / 2;
            byte[] bytes = new byte[len];
            byte high;//字节高四位
            byte low;//字节低四位
            for (int i = 0; i < len; i++) {
                //右移四位得到高位
                String hexStr = "0123456789ABCDEF";
                high = (byte) ((hexStr.indexOf(hexString.charAt(2 * i))) << 4);
                low = (byte) hexStr.indexOf(hexString.charAt(2 * i + 1));
                bytes[i] = (byte) (high | low);//高地位做或运算
            }
            return bytes;
        }

        /**
         * 二进制数组转换为二进制字符串
         */
        private StringBuilder bytes2BitStr(byte[] bArray) {
            StringBuilder builder = new StringBuilder();
            int pos;
            for (byte b : bArray) {
                //高四位
                pos = (b & 0xF0) >> 4;
                builder.append(binaryArray[pos]);
                //低四位
                pos = b & 0x0F;
                builder.append(binaryArray[pos]);
            }
            return builder;
        }
    }

    /**
     * 枚举式单例
     */
    public enum IdGeneratorEnum {
        instance;

        IdGeneratorEnum() {
            generator = new IdGenerator();
        }

        private IdGenerator generator;

        public IdGenerator getInstance() {
            return generator;
        }
    }
}